package cz.cuni.mff.ufal.volk.langr;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;

import cz.cuni.mff.ufal.volk.Text;
import cz.cuni.mff.ufal.volk.langr.NgramLanguageRecognizer.Mode;
import cz.cuni.mff.ufal.volk.services.LanguageRecognizerExtended;
import cz.cuni.mff.ufal.volk.services.LanguageRecognizerExtended.Hypothesis;

public class Test {

  /**
   * @param args
   * @throws IOException
   * @throws FileNotFoundException
   * @throws ClassNotFoundException
   */
  public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
    if (args.length < 2) {
      printHelp(System.err);
      System.exit(1);
    }

    if (args[0].equals("train")) {
      train(args);
      System.exit(0);
    }

    if (args[0].equals("recognize")) {
      recognize(args);
      System.exit(0);
    }

    if (args[0].equals("test")) {
      test(args);
      System.exit(0);
    }

    printHelp(System.err);
    System.exit(1);
  }

  private static void train(String[] args) throws FileNotFoundException,
      IOException {
    if (args.length != 4) {
      printHelp(System.err);
      System.exit(1);
    }
    NgramLanguageRecognizer recognizer = new NgramLanguageRecognizer(Mode.fromString(args[3]));
    System.out.println("training...");
    recognizer.train(new File(args[1]), 5);
    System.out.println("serializing...");
    ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(new File(args[2])));
    try {
      out.writeObject(recognizer);
    } finally {
      out.close();
    }
    System.out.println("ok");
  }

  @SuppressWarnings("unchecked")
  private static void recognize(String[] args) throws IOException,
      FileNotFoundException, ClassNotFoundException {
    if (args.length != 2) {
      printHelp(System.err);
      System.exit(1);
    }
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(args[1])));
    LanguageRecognizerExtended<Text> recognizer;
    try {
      recognizer = (LanguageRecognizerExtended<Text>)in.readObject();
    } finally {
      in.close();
    }
    Scanner cin = new Scanner(System.in);
    while (cin.hasNext()) {
      String line = cin.nextLine();
      List<Hypothesis> hypList = recognizer.recognize(new Text(null, line), 100);
      for (Hypothesis hyp : hypList) {
        System.out.printf("%s: %f", hyp.language, hyp.rating);
        if (hyp instanceof SubstitutionCipherLanguageHypothesis) {
          SubstitutionCipherLanguageHypothesis sh = (SubstitutionCipherLanguageHypothesis)hyp;
          System.out.printf(", decoded=\"%s\"", NgramDecoder.decode(sh.getSubstitutionTable(), line));
        }
        System.out.println();
      }
      System.out.println("**************");
    }
  }

  @SuppressWarnings("unchecked")
  private static void test(String[] args) throws IOException, ClassNotFoundException {
    if (args.length != 3) {
      printHelp(System.err);
      System.exit(1);
    }
    System.out.println("deserializing...");
    ObjectInputStream in = new ObjectInputStream(new FileInputStream(new File(args[1])));
    LanguageRecognizerExtended<Text> recognizer;
    try {
      recognizer = (LanguageRecognizerExtended<Text>)in.readObject();
    } finally {
      in.close();
    }
    System.out.println("testing...");
    File testDir = new File(args[2]);
    if (!testDir.isDirectory()) {
      System.err.printf("the [%s] object does not exist or is not directory", testDir.getAbsolutePath());
      System.exit(1);
    }
    File raw = null;
    for (File dir : testDir.listFiles()) {
      if (dir.isDirectory() && dir.getName().equals("raw")) {
        raw = dir;
      }
    }
    if (raw != null) {
      int total = 0, correct = 0;
      for (File corpus : raw.listFiles()) {
        if (corpus.isFile()) {
          int totalLang = 0, correctLang = 0;
          String expectedLanguageId = corpus.getName();
          Map<String, Integer> err = new HashMap<String, Integer>();
          BufferedReader reader = new BufferedReader(new FileReader(corpus));
          try {
            String line;
            while ((line = reader.readLine()) != null) {
              if (line.length() > 0) {
                List<Hypothesis> hyp = recognizer.recognize(new Text(null, line), 100);
                //LanguageHypothesis best = LanguageHypothesis.selectBest(hyp);
                Hypothesis best = Collections.max(hyp);
                totalLang++;
                if (best != null && best.language.equals(expectedLanguageId)) {
                  correctLang++;
                }else{
                  System.err.printf("[%s/%s] %s\n", expectedLanguageId, best.language, line);
                  Utils.incrementIntValue(err, best.language);
                }
              }
            }
          } finally {
            reader.close();
          }
          if (totalLang > 0) {
            System.out.printf("language [%s]: %d/%d (%f%%)", expectedLanguageId, correctLang, totalLang, 100. * correctLang / totalLang);
            if (err.size() > 0) {
              String mostFrequentErr = Utils.selectMostFrequent(err);
              int freq = err.get(mostFrequentErr);
              System.out.printf(", most frequent error: [%s] %d/%d (%f%%)", mostFrequentErr, freq, totalLang, 100. * freq / totalLang);
            }
            System.out.println();
          } else {
            System.out.printf("language [%s]: no tests found\n", expectedLanguageId);
          }
          total += totalLang;
          correct += correctLang;
        }
      }
      if (total > 0)
        System.out.printf("correctly recognized: %d/%d (%f%%)\n", correct, total, 100. * correct / total);
      else
        System.out.println("no tests found");
    }
  }

  private static void printHelp(PrintStream out) {
    out.println("langr:");
    out.println("\tlangr train input_directory output_file");
    out.println("\tlangr recognize input_file");
    out.println("\tlangr test input_folder");
  }

}
